001 /** 002 * Java Gui Builder - A library to build GUIs using an XML file. 003 * Copyright 2002, 2003 (C) François Beausoleil 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library; if not, write to the Free Software 017 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018 */ 019 020 package jgb.builder; 021 022 import jgb.builder.utils.AttributesMapAdapter; 023 import jgb.factories.swing.PackageTagHandlerFactory; 024 import org.xml.sax.*; 025 import org.xml.sax.helpers.DefaultHandler; 026 027 import javax.swing.*; 028 import javax.xml.parsers.ParserConfigurationException; 029 import javax.xml.parsers.SAXParser; 030 import javax.xml.parsers.SAXParserFactory; 031 import java.io.IOException; 032 import java.io.InputStream; 033 import java.io.PrintWriter; 034 import java.util.*; 035 import java.util.List; 036 import java.awt.*; 037 038 039 /** 040 * The class that builds the windows and controls on behalf of the application. 041 * This class instantiates the necessary objects while reading the XML source 042 * file. 043 * @since 0.2a 044 * @author Francois Beausoleil, <a href="mailto:fbos@users.sourceforge.net">fbos@users.sourceforge.net</a> 045 */ 046 public class DefaultBuilder extends DefaultHandler implements Builder { 047 private Map tagContext = new HashMap(); 048 private boolean quiet = false; 049 050 private int level; 051 private boolean verbose = false; 052 053 private final List resolvers = new LinkedList(); 054 private PrintWriter logStream = new PrintWriter(System.out); 055 056 /** 057 * Instantiate a <code>Builder</code> and use the default 058 * {@link jgb.builder.TagHandlerFactory TagHandlerFactory}. 059 * The default factory is {@link jgb.factories.swing.PackageTagHandlerFactory PackageTagHandlerFactory}. 060 */ 061 public DefaultBuilder() { 062 this(new PackageTagHandlerFactory("jgb.handlers.swing", "TagHandler")); 063 } 064 065 /** 066 * Instantiate a <code>Builder</code> with the specified 067 * {@link jgb.builder.TagHandlerFactory TagHandlerFactory}. 068 * The <code>Builder</code> uses this factory to get all of it's tag handlers. 069 * All tags must implement {@link jgb.builder.TagHandler TagHandler}. 070 */ 071 public DefaultBuilder(final TagHandlerFactory tagsFactory) { 072 Stack handlersFactoryStack = new Stack(); 073 handlersFactoryStack.push(tagsFactory); 074 tagContext.put(TagHandler.TAG_HANDLER_FACTORY_STACK_KEY, handlersFactoryStack); 075 } 076 077 public void setQuiet(boolean quiet) { 078 this.quiet = quiet; 079 } 080 081 public Component build(InputStream in) throws IOException, SAXException { 082 if (null == in) { 083 throw new IllegalArgumentException("in must not be null"); 084 } 085 086 return build(new InputSource(in)); 087 } 088 089 public Component build(InputSource in) throws IOException, SAXException { 090 if (null == in) { 091 throw new IllegalArgumentException("InputSource cannot be be null"); 092 } else if (null == in.getByteStream() 093 && null == in.getCharacterStream() 094 && null == in.getSystemId() 095 && null == in.getPublicId()) { 096 throw new IllegalArgumentException("InputSource should have at least one non-null source"); 097 } 098 099 SAXParserFactory factory = SAXParserFactory.newInstance(); 100 factory.setValidating(true); 101 SAXParser parser = null; 102 try { 103 parser = factory.newSAXParser(); 104 } catch (ParserConfigurationException e) { 105 throw new SAXException("No validating parser present on this system", e); 106 } 107 108 tagContext.put(TagHandler.WINDOW_CONTEXT_KEY, this); 109 tagContext.put(TagHandler.OBJECTS_MAP_KEY, new HashMap()); 110 parser.parse(in, this); 111 return (JWindow)tagContext.get(TagHandler.CURRENT_WINDOW_KEY); 112 } 113 114 public void setLoggingStream(PrintWriter logStream) { 115 this.logStream = logStream; 116 } 117 118 public Object getObject(String name) { 119 return (((Map)tagContext.get(TagHandler.OBJECTS_MAP_KEY)).get(name)); 120 } 121 122 /** 123 * Initiates processing of the given element. 124 * This method will use the factory to get a {@link jgb.builder.TagHandler TagHandler} 125 * to process this tag.<br /> 126 * Tag specific operations may occur during this method call. 127 */ 128 public void startElement(java.lang.String namespaceURI, 129 java.lang.String localName, 130 java.lang.String qName, 131 Attributes atts) throws SAXException { 132 if (verbose) { 133 logStream.print(levelAsSpaces() + "<" + qName); 134 if (null != atts.getValue("id")) { 135 logStream.println(" id='" + atts.getValue("id") + "'>"); 136 } else { 137 logStream.println(">"); 138 } 139 logStream.flush(); 140 } 141 142 TagHandler handler = getHandlerFor(qName); 143 handler.startElement(qName, tagContext, new AttributesMapAdapter(atts)); 144 145 level++; 146 } 147 148 /** 149 * Terminates processing of the given element. 150 * This method will use the factory to get a {@link jgb.builder.TagHandler TagHandler} 151 * to process this tag.<br /> 152 * Tag specific operations may occur during this method call. 153 */ 154 public void endElement(String namespaceURI, String localName, String qName) throws SAXException { 155 level--; 156 if (level < 0) { 157 level = 0; 158 } 159 160 if (verbose) { 161 logStream.println(levelAsSpaces() + "</" + qName + ">"); 162 logStream.flush(); 163 } 164 165 TagHandler handler = getHandlerFor(qName); 166 handler.endElement(qName, tagContext); 167 } 168 169 /** 170 * Displays a warning message on the console, or no-op if in quiet mode. 171 */ 172 public void warning(SAXParseException e) throws SAXException { 173 if (!quiet) { 174 logStream.print("WARNING: line: "); 175 logStream.print(e.getLineNumber()); 176 logStream.print(", column: "); 177 logStream.print(e.getColumnNumber()); 178 logStream.println(" - "); 179 logStream.print(e.getMessage()); 180 logStream.print("(\""); 181 logStream.print(e.getPublicId()); 182 logStream.print("\" \""); 183 logStream.print(e.getSystemId()); 184 logStream.print("\")"); 185 logStream.flush(); 186 } 187 } 188 189 /** 190 * Displays a fatal error message on the console, or no-op if in quiet mode. 191 * In all cases, it throws a <code>SAXException<code> to signify that processing 192 * should stop. 193 */ 194 public void fatalError(SAXParseException e) throws SAXException { 195 if (!quiet) { 196 logStream.print("FATAL: line: "); 197 logStream.print(e.getLineNumber()); 198 logStream.print(", column: "); 199 logStream.print(e.getColumnNumber()); 200 logStream.println(" - "); 201 logStream.print(e.getMessage()); 202 logStream.print("(\""); 203 logStream.print(e.getPublicId()); 204 logStream.print("\" \""); 205 logStream.print(e.getSystemId()); 206 logStream.print("\")"); 207 logStream.flush(); 208 } 209 210 throw new SAXException("Fatal Error during parse", e); 211 } 212 213 /** 214 * Displays an error message on the console, or no-op if in quiet mode. 215 * In all cases, it throws a <code>SAXException</code> to signify that processing 216 * should stop. 217 */ 218 public void error(SAXParseException e) throws SAXException { 219 if (!quiet) { 220 logStream.print("ERROR: line: "); 221 logStream.print(e.getLineNumber()); 222 logStream.print(", column: "); 223 logStream.print(e.getColumnNumber()); 224 logStream.println(" - "); 225 logStream.print(e.getMessage()); 226 logStream.print("(\""); 227 logStream.print(e.getPublicId()); 228 logStream.print("\" \""); 229 logStream.print(e.getSystemId()); 230 logStream.print("\")"); 231 logStream.flush(); 232 } 233 234 throw new SAXException("Error during parse", e); 235 } 236 237 /** 238 * Saves the document locator that the parser might provide. If it does so, 239 * we are in business, else, we're toast... 240 */ 241 public void setDocumentLocator(Locator locator) { 242 tagContext.put(TagHandler.DOCUMENT_LOCATOR_KEY, locator); 243 super.setDocumentLocator(locator); 244 } 245 246 public void setVerbose(boolean b) { 247 verbose = b; 248 } 249 250 /** 251 * Returns the {@link jgb.builder.TagHandler TagHandler} associated 252 * with the named tag. 253 * This method uses the {@link jgb.builder.TagHandlerFactory TagHandlerFactory} that 254 * was passed-in during instantiation to get the proper tag. 255 * @throws SAXException If a handler for the named tag cannot be found. 256 */ 257 private TagHandler getHandlerFor(String qName) throws SAXException { 258 Stack handlersFactory = (Stack)tagContext.get(TagHandler.TAG_HANDLER_FACTORY_STACK_KEY); 259 if (handlersFactory.isEmpty()) { 260 EmptyStackException e = new EmptyStackException(); 261 throw new SAXException("stack empty while processing <" + qName + ">", e); 262 } 263 264 TagHandlerFactory factory = (TagHandlerFactory)handlersFactory.peek(); 265 try { 266 return factory.getHandlerFor(qName); 267 } catch (Exception e) { 268 throw new SAXException("Could not instantiate TagHandler for tag " + qName + ".", e); 269 } 270 } 271 272 private String levelAsSpaces() { 273 StringBuffer buf = new StringBuffer(); 274 for (int i = 0; i < level; i++) { 275 buf.append(" "); 276 } 277 278 return buf.toString(); 279 } 280 281 public void addResolver(EntityResolver resolver) { 282 resolvers.add(resolver); 283 } 284 285 public InputSource resolveEntity(String publicId, String systemId) throws SAXException { 286 try { 287 for (Iterator iterator = resolvers.iterator(); iterator.hasNext();) { 288 EntityResolver resolver = (EntityResolver)iterator.next(); 289 InputSource entity = resolver.resolveEntity(publicId, systemId); 290 if (null != entity) { 291 return entity; 292 } 293 } 294 295 return super.resolveEntity(publicId, systemId); 296 } catch (IOException e) { 297 throw new SAXException( 298 "Unable to resolve '" + publicId + "' " + 299 "('" + systemId + "')", e); 300 } 301 } 302 }